﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Runtime.InteropServices;
using System.Configuration;
using System.Drawing;
using System.Drawing.Drawing2D;
using System.Drawing.Imaging;
using System.Diagnostics;

namespace ArcsoftFaceTest
{
    public class AmFaceVerify
    {
        public IntPtr detectEngine = IntPtr.Zero;

        //人脸识别引擎
        public IntPtr regcognizeEngine = IntPtr.Zero;

        public static string appId = ConfigurationManager.AppSettings["appId"].ToString();
        public static string sdkKeyReco = ConfigurationManager.AppSettings["sdkKeyFR"].ToString();
        public static string sdkKeyDetect = ConfigurationManager.AppSettings["sdkKeyFD"].ToString();
        private struct AFR_FSDK_FACEMODEL
        {
            public IntPtr pbFeature;	// The extracted features

            public int lFeatureSize;	// The size of pbFeature
        }
        private struct AFR_FSDK_FACEINPUT
        {
            public MRECT rcFace;	                   // The bounding box of face

            public int lfaceOrient;                 // The orientation of face
        }
        private struct AFR_FSDK_Version
        {
            public int lCodebase;
            public int lMajor;
            public int lMinor;
            public int lBuild;
            public int lFeatureLevel;
            public IntPtr Version;
            public IntPtr BuildDate;
            public IntPtr CopyRight;
        }
        private struct AFD_FSDK_FACERES
        {
            public int nFace;                     // number of faces detected

            public IntPtr rcFace;                        // The bounding box of face

            public IntPtr lfaceOrient;                   // the angle of each face
        }
        private enum AFD_FSDK_OrientPriority
        {
            AFD_FSDK_OPF_0_ONLY=1,
            AFD_FSDK_OPF_90_ONLY=2,
            AFD_FSDK_OPF_270_ONLY=3,
            AFD_FSDK_OPF_180_ONLY=4,
            AFD_FSDK_OPF_0_HIGHER_EXT=5
        }
        private enum AFD_FSDK_OrientCode
        {
            AFD_FSDK_FOC_0 = 1,
            AFD_FSDK_FOC_90 = 2,
            AFD_FSDK_FOC_270 = 3,
            AFD_FSDK_FOC_180 = 4,
            AFD_FSDK_FOC_30 = 5,
            AFD_FSDK_FOC_60 = 6,
            AFD_FSDK_FOC_120 = 7,
            AFD_FSDK_FOC_150 = 8,
            AFD_FSDK_FOC_210 = 9,
            AFD_FSDK_FOC_240 = 10,
            AFD_FSDK_FOC_300 = 11,
            AFD_FSDK_FOC_330 = 12
        }

        private struct ASVLOFFSCREEN
        {
            public int u32PixelArrayFormat;

            public int i32Width;

            public int i32Height;

            [MarshalAs(UnmanagedType.ByValArray, SizeConst = 4, ArraySubType = UnmanagedType.SysUInt)]
            public IntPtr[] ppu8Plane;

            [MarshalAs(UnmanagedType.ByValArray, SizeConst = 4, ArraySubType = UnmanagedType.SysUInt)]
            public int[] pi32Pitch;
        }
        public bool AFD_Init(){
            int detectSize = 40 * 1024 * 1024;
            int recognizeSize = 40 * 1024 * 1024;
            int nScale = 16;
            int nMaxFaceNum = 10;
            IntPtr pMem = Marshal.AllocHGlobal(detectSize);
            IntPtr pMemDetect = Marshal.AllocHGlobal(recognizeSize);
            int retCode = AFD_FSDK_InitialFaceEngine(appId, sdkKeyDetect, pMem, detectSize, ref detectEngine, (int)AFD_FSDK_OrientPriority.AFD_FSDK_OPF_0_HIGHER_EXT, nScale, nMaxFaceNum);
            if (retCode != 0)
            {
                return false;
            }
            else
            {
                retCode = AmFaceVerify.AFR_FSDK_InitialEngine(appId, sdkKeyReco, pMemDetect, recognizeSize, ref regcognizeEngine);
                if (retCode != 0)
                    return false;
                else
                    return true;
            }
        }
        public void Uninit() {
            AFR_FSDK_UninitialEngine(regcognizeEngine);
            AFD_FSDK_UninitialFaceEngine(detectEngine);
        }
        public byte[] detectFace(string path) {
            Image image = Image.FromFile(path);
            byte[] ret = detectAndExtractFeature(image);
            image.Dispose();
            return ret;
        }

        private byte[] detectAndExtractFeature(Image imageParam)
        {
            byte[] feature = null;
            Bitmap bitmap = new Bitmap(imageParam);
            IntPtr imageDataPtr = IntPtr.Zero;
            ASVLOFFSCREEN offInput;
            AFD_FSDK_FACERES faceRes;
            byte[] imageData;
            try
            {
                int width = 0;

                int height = 0;

                int pitch = 0;

                Stopwatch watchTime = new Stopwatch();
                watchTime.Start();
                imageData = getBGR(bitmap, ref width, ref height, ref pitch);
                imageDataPtr = Marshal.AllocHGlobal(imageData.Length);
                Marshal.Copy(imageData, 0, imageDataPtr, imageData.Length);
                offInput = new ASVLOFFSCREEN();
                faceRes = new AFD_FSDK_FACERES();
                IntPtr offInputPtr = IntPtr.Zero;
                offInput.u32PixelArrayFormat = 513;
                offInput.ppu8Plane = new IntPtr[4];
                offInput.ppu8Plane[0] = imageDataPtr;
                offInput.i32Width = width;
                offInput.i32Height = height;
                offInput.pi32Pitch = new int[4];
                offInput.pi32Pitch[0] = pitch;
                offInputPtr = Marshal.AllocHGlobal(Marshal.SizeOf(offInput));
                Marshal.StructureToPtr(offInput, offInputPtr, false);
                IntPtr faceResPtr = Marshal.AllocHGlobal(Marshal.SizeOf(faceRes));
                int detectResult = AFD_FSDK_StillImageFaceDetection(detectEngine, offInputPtr, ref faceResPtr);
                watchTime.Stop();
                //Console.WriteLine(String.Format("检测耗时:{0}ms", watchTime.ElapsedMilliseconds));
                object obj = Marshal.PtrToStructure(faceResPtr, typeof(AFD_FSDK_FACERES));
                faceRes = (AFD_FSDK_FACERES)obj;
                Console.WriteLine("  Face Count:{0}", faceRes.nFace);

                if (faceRes.nFace > 0)
                {

                    AFR_FSDK_FACEINPUT faceResult = new AFR_FSDK_FACEINPUT();
                    int orient = (int)Marshal.PtrToStructure(faceRes.lfaceOrient, typeof(int));

                    faceResult.lfaceOrient = orient;

                    faceResult.rcFace = new MRECT();

                    MRECT rect = (MRECT)Marshal.PtrToStructure(faceRes.rcFace, typeof(MRECT));

                    faceResult.rcFace = rect;

                    IntPtr faceResultPtr = Marshal.AllocHGlobal(Marshal.SizeOf(faceResult));

                    Marshal.StructureToPtr(faceResult, faceResultPtr, false);

                    AFR_FSDK_FACEMODEL localFaceModels = new AFR_FSDK_FACEMODEL();

                    IntPtr localFaceModelsPtr = Marshal.AllocHGlobal(Marshal.SizeOf(localFaceModels));

                    int extractResult = AmFaceVerify.AFR_FSDK_ExtractFRFeature(regcognizeEngine, offInputPtr, faceResultPtr, localFaceModelsPtr);

                    Marshal.FreeHGlobal(faceResultPtr);

                    if (offInputPtr != null) Marshal.FreeHGlobal(offInputPtr);

                    object objFeature = Marshal.PtrToStructure(localFaceModelsPtr, typeof(AFR_FSDK_FACEMODEL));

                    Marshal.FreeHGlobal(localFaceModelsPtr);

                    localFaceModels = (AFR_FSDK_FACEMODEL)objFeature;

                    feature = new byte[localFaceModels.lFeatureSize];

                    Marshal.Copy(localFaceModels.pbFeature, feature, 0, localFaceModels.lFeatureSize);

                    localFaceModels = new AFR_FSDK_FACEMODEL();
                }
                bitmap.Dispose();
                imageData = null;
                Marshal.FreeHGlobal(imageDataPtr);
                offInput = new ASVLOFFSCREEN();
                faceRes = new AFD_FSDK_FACERES();
            }
            catch (ArgumentNullException) { }
            catch (Exception e)
            {
                Console.WriteLine(e.StackTrace);
            }
            finally {

            }
            return feature;
        }

        public float matchFace(byte[] firstFeature, byte[] secondFeature)
        {
            if (firstFeature == null || secondFeature == null) return 0;
            float similar = 0f;

            AFR_FSDK_FACEMODEL localFaceModels = new AFR_FSDK_FACEMODEL();

            IntPtr firstFeaturePtr = Marshal.AllocHGlobal(firstFeature.Length);

            Marshal.Copy(firstFeature, 0, firstFeaturePtr, firstFeature.Length);

            localFaceModels.lFeatureSize = firstFeature.Length;

            localFaceModels.pbFeature = firstFeaturePtr;

            IntPtr secondFeaturePtr = Marshal.AllocHGlobal(secondFeature.Length);

            Marshal.Copy(secondFeature, 0, secondFeaturePtr, secondFeature.Length);

            AFR_FSDK_FACEMODEL localFaceModels2 = new AFR_FSDK_FACEMODEL();

            localFaceModels2.lFeatureSize = secondFeature.Length;

            localFaceModels2.pbFeature = secondFeaturePtr;

            IntPtr firstPtr = Marshal.AllocHGlobal(Marshal.SizeOf(localFaceModels));

            Marshal.StructureToPtr(localFaceModels, firstPtr, false);

            IntPtr secondPtr = Marshal.AllocHGlobal(Marshal.SizeOf(localFaceModels2));

            Marshal.StructureToPtr(localFaceModels2, secondPtr, false);

            Stopwatch stopwatch = new Stopwatch();

            stopwatch.Start();

            int result = AmFaceVerify.AFR_FSDK_FacePairMatching(regcognizeEngine, firstPtr, secondPtr, ref similar);

            stopwatch.Stop();

            Console.WriteLine("相似度:{0},耗时:{1}ms", similar.ToString(),stopwatch.ElapsedMilliseconds.ToString());
            localFaceModels = new AFR_FSDK_FACEMODEL();

            Marshal.FreeHGlobal(firstFeaturePtr);

            Marshal.FreeHGlobal(secondFeaturePtr);

            Marshal.FreeHGlobal(firstPtr);

            Marshal.FreeHGlobal(secondPtr);

            localFaceModels2 = new AFR_FSDK_FACEMODEL();
            return similar;
        }

        private byte[] getBGR(Bitmap image, ref int width, ref int height, ref int pitch)
        {
            //Bitmap image = new Bitmap(imgPath);

            const PixelFormat PixelFormat = PixelFormat.Format24bppRgb;

            BitmapData data = image.LockBits(new Rectangle(0, 0, image.Width, image.Height), ImageLockMode.ReadOnly, PixelFormat);

            IntPtr ptr = data.Scan0;

            int ptr_len = data.Height * Math.Abs(data.Stride);

            byte[] ptr_bgr = new byte[ptr_len];

            Marshal.Copy(ptr, ptr_bgr, 0, ptr_len);

            width = data.Width;

            height = data.Height;

            pitch = Math.Abs(data.Stride);

            int line = width * 3;

            int bgr_len = line * height;

            byte[] bgr = new byte[bgr_len];

            for (int i = 0; i < height; ++i)
            {
                Array.Copy(ptr_bgr, i * pitch, bgr, i * line, line);
            }

            pitch = line;

            image.UnlockBits(data);

            return bgr;
        }

        private static Bitmap CutFace(Bitmap srcImage, int StartX, int StartY, int iWidth, int iHeight)
        {
            if (srcImage == null)
            {
                return null;
            }
            int w = srcImage.Width;
            int h = srcImage.Height;
            if (StartX >= w || StartY >= h)
            { 
                return null;
            }
            if (StartX + iWidth > w)
            {
                iWidth = w - StartX;
            }
            if (StartY + iHeight > h)
            {
                iHeight = h - StartY;
            }
            try
            {
                Bitmap bmpOut = new Bitmap(iWidth, iHeight, PixelFormat.Format24bppRgb);
                Graphics g = Graphics.FromImage(bmpOut);
                g.DrawImage(srcImage, new Rectangle(0, 0, iWidth, iHeight), new Rectangle(StartX, StartY, iWidth, iHeight), GraphicsUnit.Pixel);
                g.Dispose();
                return bmpOut;
            }
            catch
            { 
            return null;
            }
        }
        private  Image DrawRectangleInPicture(Image bmp, Point p0, Point p1, Color RectColor, int LineWidth, DashStyle ds)
        { 
            if (bmp == null) return null;
            Graphics g = Graphics.FromImage(bmp);
            Brush brush = new SolidBrush(RectColor);
            Pen pen = new Pen(brush, LineWidth);
            pen.DashStyle = ds;
            g.DrawRectangle(pen, new Rectangle(p0.X, p0.Y, Math.Abs(p0.X - p1.X), Math.Abs(p0.Y - p1.Y))); 
            g.Dispose();
            return bmp;
        }
        /**
        * 初始化人脸检测引擎
        * @return 初始化人脸检测引擎
        */
        [DllImport("libarcsoft_fsdk_face_detection.dll", CallingConvention = CallingConvention.Cdecl)]
        public static extern int AFD_FSDK_InitialFaceEngine(string appId, string sdkKey, IntPtr pMem, int lMemSize, ref IntPtr pEngine, int iOrientPriority, int nScale, int nMaxFaceNum);

        [DllImport("libarcsoft_fsdk_face_detection.dll", CallingConvention = CallingConvention.Cdecl)]
        public static extern int AFD_FSDK_UninitialFaceEngine(IntPtr pEngine);

        /**
        * 获取人脸检测 SDK 版本信息
        * @return 获取人脸检测SDK 版本信息
        */
        [DllImport("libarcsoft_fsdk_face_detection.dll", CallingConvention = CallingConvention.Cdecl)]
        public static extern IntPtr AFD_FSDK_GetVersion(IntPtr pEngine);

        /**
        * 根据输入的图像检测出人脸位置，一般用于静态图像检测
        * @return 人脸位置
        */
        [DllImport("libarcsoft_fsdk_face_detection.dll", CallingConvention = CallingConvention.Cdecl)]
        public static extern int AFD_FSDK_StillImageFaceDetection(IntPtr pEngine, IntPtr offline, ref IntPtr faceRes);

        [DllImport("libarcsoft_fsdk_face_recognition.dll", CallingConvention = CallingConvention.Cdecl)]
        public static extern int AFD_FSDK_UninitialEngine(IntPtr pEngine);
        /**
        * 初始化人脸识别引擎
        * @return 初始化人脸识别引擎
        */
        [DllImport("libarcsoft_fsdk_face_recognition.dll", CallingConvention = CallingConvention.Cdecl)]
        public static extern int AFR_FSDK_InitialEngine(string appId, string sdkKey, IntPtr pMem, int lMemSize, ref IntPtr pEngine);

        /**
        * 获取人脸识别SDK 版本信息
        * @return 获取人脸识别SDK 版本信息
        */
        [DllImport("libarcsoft_fsdk_face_recognition.dll", CallingConvention = CallingConvention.Cdecl)]
        public static extern IntPtr AFR_FSDK_GetVersion(IntPtr pEngine);

        /**
        * 提取人脸特征
        * @return 提取人脸特征
        */
        [DllImport("libarcsoft_fsdk_face_recognition.dll", CallingConvention = CallingConvention.Cdecl)]
        public static extern int AFR_FSDK_ExtractFRFeature(IntPtr pEngine, IntPtr offline, IntPtr faceResult, IntPtr localFaceModels);

        /**
        * 获取相似度
        * @return 获取相似度
        */
        [DllImport("libarcsoft_fsdk_face_recognition.dll", CallingConvention = CallingConvention.Cdecl)]
        public static extern int AFR_FSDK_FacePairMatching(IntPtr pEngine, IntPtr faceModels1, IntPtr faceModels2, ref float fSimilScore);

        [DllImport("libarcsoft_fsdk_face_recognition.dll", CallingConvention = CallingConvention.Cdecl)]
        public static extern int AFR_FSDK_UninitialEngine(IntPtr pEngine);

        #region delete
        ///**
        // *  创建人脸检测引擎
        // *  @param [in] model_path 模型文件夹路径
        // *  @param [out] engine 创建的人脸检测引擎
        // *  @return =0 表示成功，<0 表示错误码。
        // */
        //[DllImport("AmFaceDet.dll", CallingConvention = CallingConvention.Cdecl)]
        //public static extern int AmCreateFaceDetectEngine(string modelPath, ref IntPtr faceDetectEngine);

        ///**
        // *  创建人脸识别引擎
        // *  @param [in] model_path 模型文件夹路径
        // *  @param [out] engine 创建的人脸识别引擎
        // *  @return =0 表示成功，<0 表示错误码。
        // */
        //[DllImport("AmFaceRec.dll", CallingConvention = CallingConvention.Cdecl)]
        //public static extern int AmCreateFaceRecogniseEngine(string modelPath, ref IntPtr facRecogniseeEngine);

        ///**
        // *  创建人脸比对别引擎
        // *  @param [in] model_path 模型文件夹路径
        // *  @param [out] engine 创建的人脸比对引擎
        // *  @return =0 表示成功，<0 表示错误码。
        // */
        //[DllImport("AmFaceCompare.dll", CallingConvention = CallingConvention.Cdecl)]
        //public static extern int AmCreateFaceCompareEngine(ref IntPtr facCompareEngine);

        ///**
        // *  设置人脸引擎参数
        // *  @param [in] engine 人脸引擎
        // *  @param [in] param 人脸参数
        // */
        //[DllImport("AmFaceDet.dll", CallingConvention = CallingConvention.Cdecl)]
        //public static extern void AmSetParam(IntPtr faceDetectEngine, [MarshalAs(UnmanagedType.LPArray)] [In] TFaceParams[] setFaceParams);

        ///**
        // * 人脸检测
        // * @param [in] engine 人脸引擎
        // * @param [in] bgr 图像数据，BGR格式
        // * @param [in] width 图像宽度
        // * @param [in] height 图像高度
        // * @param [in] pitch 图像数据行字节数
        // * @param [in,out] faces 人脸结构体数组，元素个数应等于期望检测人脸个数
        // * @param [in] face_count 期望检测人脸个数
        // * @return >=0 表示实际检测到的人脸数量，<0 表示错误码。
        // */
        //[DllImport("AmFaceDet.dll", CallingConvention = CallingConvention.Cdecl)]
        //public static extern int AmDetectFaces(IntPtr faceDetectEngine, [MarshalAs(UnmanagedType.LPArray)] [In] byte[] image, int width, int height, int pitch, [MarshalAs(UnmanagedType.LPArray)] [In][Out] TAmFace[] faces, int face_count);

        ///**
        // * 抽取人脸特征
        // * @param [in] engine 人脸引擎
        // * @param [in] bgr 图像数据，BGR格式
        // * @param [in] width 图像宽度
        // * @param [in] height 图像高度
        // * @param [in] pitch 图像数据行字节数
        // * @param [in] face 人脸结构体
        // * @param [out] feature 人脸特征
        // * @return =0 表示成功，<0 表示错误码。
        // */
        //[DllImport("AmFaceRec.dll", CallingConvention = CallingConvention.Cdecl)]
        ////public static extern int AmExtractFeature(IntPtr faceEngine, [MarshalAs(UnmanagedType.LPArray)] [In] byte[] image, int width, int height, int pitch, [MarshalAs(UnmanagedType.LPArray)] [In] TAmFace[] faces, ref byte[] feature);
        //public static extern int AmExtractFeature(IntPtr facRecogniseeEngine, [MarshalAs(UnmanagedType.LPArray)] [In] byte[] image, int width, int height, int pitch, [MarshalAs(UnmanagedType.LPArray)] [In] TAmFace[] faces, [MarshalAs(UnmanagedType.LPArray)] [Out] byte[] feature);

        ///**
        // * 比对两个人脸特征相似度
        // * @param [in] engine 人脸引擎
        // * @param [in] feature1 人脸特征1
        // * @param [in] feature2 人脸特征2
        // * @return 人脸相似度
        // */
        //[DllImport("AmFaceCompare.dll", CallingConvention = CallingConvention.Cdecl)]
        //public static extern float AmCompare(IntPtr facCompareEngine, byte[] feature1, byte[] feature2);
        #endregion
    }
}
